package com.engine.game;

import java.util.ArrayList;
import java.util.HashMap;

import aurelienribon.tweenengine.TweenManager;

import com.badlogic.gdx.math.Vector2;
import com.component.game.AIComponent;
import com.component.game.Component;
import com.component.game.InputComponent;
import com.component.game.NullAIComponent;
import com.component.game.NullInputComponent;
import com.component.game.NullPhysicsComponent;
import com.component.game.PhysicsComponent;
import com.component.game.RenderComponent;
import com.movement.game.Movable;
import com.movement.game.Movement;
import com.movement.game.NullMovement;
import com.physics.game.PhysicsWorld;
import com.render.game.RenderType;
import com.render.game.Stage;

public class GameObject implements GameEntity{
	
	
	public GameObject(String name, GameState state, InputComponent input, AIComponent ai, PhysicsComponent physics, RenderComponent render, boolean isMovable, int stage, RenderType rendertype, int renderIndex){
		
		this.stageID = stage;
		this.name = name;
		this.renderType = rendertype;
		
		this.setState(state);
		
		this.setInput(input);
		this.setAi(ai);
		this.setPhysics(physics);
		this.setRender(render);
		
		this.eventMap = new HashMap<Component, ArrayList<MessageInfo>>();
		
		this.eventMap.put(Component.Input, new ArrayList<MessageInfo>());
		this.eventMap.put(Component.AI, new ArrayList<MessageInfo>());
		this.eventMap.put(Component.Physics, new ArrayList<MessageInfo>());
		this.eventMap.put(Component.Render, new ArrayList<MessageInfo>());
		
		
		if(renderType != RenderType.no_type){
			
		
		if(renderIndex == -1){
			state.pushObjectToRenderer(name, renderType, stageID);
		}
		else{
			state.pushObjectToRenderer(name, renderType, renderIndex, stageID);
		}
		
		}
		
		this.isMovable = isMovable;
		
		
		this.isDead = false;
		
		if(this.isMovable){
			
			this.move = new Movement();
			
		}
		else
		{
			this.move = new NullMovement();
		}
	}
	
	
	
	public GameObject(String name, GameState state, InputComponent input, PhysicsComponent physics, RenderComponent render, boolean isMovable, int stage, RenderType rendertype, int renderIndex){
		
		this(name, state, input, new NullAIComponent(), physics, render, isMovable, stage, rendertype, renderIndex);
	}
	
	public GameObject(String name, GameState state, InputComponent input, AIComponent ai, RenderComponent render, boolean isMovable, int stage, RenderType rendertype, int renderIndex){
		
		this(name, state, input, ai, new NullPhysicsComponent(), render, isMovable, stage, rendertype, renderIndex);
	}
	
	public GameObject(String name, GameState state, AIComponent ai, PhysicsComponent physics, RenderComponent render, boolean isMovable, int stage, RenderType rendertype, int renderIndex){
		
		this(name, state, new NullInputComponent(), ai, physics, render, isMovable, stage, rendertype, renderIndex);
	}
	
	
	
	

	public InputComponent getInput() {
		return input;
	}

	public void setInput(InputComponent input) {
		this.input = input;
	}

	public AIComponent getAi() {
		return ai;
	}


	public void setAi(AIComponent ai) {
		this.ai = ai;
	}

	public PhysicsComponent getPhysics() {
		return physics;
	}


	public void setPhysics(PhysicsComponent physics) {
		this.physics = physics;
		
		this.physics.bodyCreation(this);
	}

	public RenderComponent getRender() {
		return render;
	}


	public void setRender(RenderComponent render) {
		this.render = render;
	}
	
	
	
	public Vector2 getWorldCoordinates(Vector2 v){
		return this.state.getWorldCoordinates(v);
    }
	
	
	
	public void lerp(float alpha){
		if(isMovable){
			updateMove((physics.getCenterBodyX()*alpha + move.getCenterX()*(1 - alpha)), (physics.getCenterBodyY()*alpha + move.getCenterY()*(1 - alpha)), 
					(physics.getBodyAngle()*alpha + move.getAngle()*(1 - alpha)));
		}
	}
	
	public void receiveMessage(Event event){
		this.eventMap.get(event.getComponent()).add(new MessageInfo(event.getSender(), event.getMessage()));
	}
	
	public void generate(String message, Component component){
		this.state.pushEvent(Event.createEvent(this.name, message, component));
	}
	
	public void generate(String message, String receiver, Component component){
		
		
		this.state.pushEvent(Event.createEvent(this.name, receiver, message, component));
	}
	
	
	public PhysicsWorld getPhysicsWorld(){
		
		return this.state.getPhysicsWorld();
		
	}
	
	public Stage getStage(){
		return this.state.getStage(this.stageID);
	}
	
	
	public TweenManager getTweenManager(){
		return this.state.getTweenManager();
	}
	
	public ArrayList<MessageInfo> getInputMessages(){
		return this.eventMap.get(Component.Input);
	}
	
	public ArrayList<MessageInfo> getAIMessages(){
		return this.eventMap.get(Component.AI);
	}
	
	public ArrayList<MessageInfo> getPhysicsMessages(){
		return this.eventMap.get(Component.Physics);
	}
	
	public ArrayList<MessageInfo> getRenderMessages(){
		return this.eventMap.get(Component.Render);
	}
	
	public void update() throws InstantiationException, IllegalAccessException, ClassNotFoundException{
	    input.update(this);
	    ai.update(this);
	    physics.update(this);
	    
	    if(this.isMovable){
	    	move.update(physics.getCenterBodyX(), physics.getCenterBodyY(), physics.getBodyAngle());
	    
	    }
	    
	    render.update(this);
	    
	    this.eventMap.get(Component.Input).clear();
		this.eventMap.get(Component.AI).clear();
		this.eventMap.get(Component.Physics).clear();
		this.eventMap.get(Component.Render).clear();
	}
	
	
	public void die(){
		this.state.sendToGraveyard(name);
		this.isDead = true;
	}
	
	

	public GameState getState() {
		return state;
	}

	public void setState(GameState state) {
		this.state = state;
	}
	
	private void clear(){
		
		
		this.eventMap.get(Component.Input).clear();
		this.eventMap.get(Component.AI).clear();
		this.eventMap.get(Component.Physics).clear();
		this.eventMap.get(Component.Render).clear();
		
		this.eventMap = null;
		
		this.name = null;
		
		this.move = null;
		
	}

	
	public String name;
	
	public int stageID;
	
	public RenderType renderType;
	
	public boolean isMovable;
	
	public boolean isDead;

	private InputComponent input;
	private AIComponent ai;
	private PhysicsComponent physics;
	private RenderComponent render;
	
	private HashMap<Component, ArrayList<MessageInfo>> eventMap;
	
	private GameState state;
	
	private Movable move;

	
	public void dispose(){
		
		state.deleteObjectFromRenderer(name, renderType, stageID);
		
		this.clear();
		
		this.input.dispose();
		this.ai.dispose();
		this.physics.dispose();
		
		this.render.dispose();
		
		this.input = null;
		this.ai = null;
		this.physics = null;
		this.render = null;
	}

	public Movable getMove() {
		return move;
	}

	public void updateMove(float x, float y, float angle) {
		this.move.update(x, y, angle);
	}



	@Override
	public boolean getIsDead() {
		
		return this.isDead;
	}



	@Override
	public int getStageID() {
		
		return this.stageID;
	}

	
}
